using System;
using System.IO;
using System.Windows.Forms;
using OFX.Aggregator.Infrastructure;

namespace OFX.Aggregator.Implementation
{
	public sealed class StatementProcessingWorkflow : IStatementProcessingWorkflow
	{
		#region Implementation of IStatementProcessingWorkflow

		public void PerformWorkflow(StatementSource source, IStatementAction action)
		{
			if (this.WorkflowStarted != null)
				this.WorkflowStarted(this, EventArgs.Empty);

			var dateStart = new DateTime(2008, 8, 5, 0, 0, 0, DateTimeKind.Local);
			var asyncStatementRetrieval = source.Retrieval as IAsyncStatementRetrieval;
			if (asyncStatementRetrieval != null)
			{
				// Asynchronous retrieval
				asyncStatementRetrieval.BeginGetStatement(dateStart, null, this.onEndRetrieveStatement,
				                                          new object[] {source, action});
			}
			else
			{
				// Synchronous retrieval
				try
				{
					Statement statement = source.Retrieval.GetStatement(dateStart, null);

					interpretStatement(source, statement, action);
				}
				catch (Exception ex)
				{
					MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Stop);
				}
				finally
				{
					if (this.WorkflowFinished != null)
						this.WorkflowFinished(this, EventArgs.Empty);
				}
			}
		}

		public event EventHandler WorkflowStarted;
		public event EventHandler WorkflowFinished;

		private void onEndRetrieveStatement(IAsyncResult asyncResult)
		{
			var state = (object[]) asyncResult.AsyncState;
			var source = (StatementSource) state[0];
			var action = (IStatementAction) state[1];

			try
			{
				Statement statement = ((IAsyncStatementRetrieval) source.Retrieval).EndGetStatement(asyncResult);

				interpretStatement(source, statement, action);
			}
			catch (Exception ex)
			{
				MessageBox.Show(getInnermostException(ex).Message, Application.ProductName, MessageBoxButtons.OK,
				                MessageBoxIcon.Stop);
			}
			finally
			{
				if (this.WorkflowFinished != null)
					this.WorkflowFinished(this, EventArgs.Empty);
			}
		}

		private static Exception getInnermostException(Exception ex)
		{
			if (ex != null)
			{
				while (ex.InnerException != null)
					ex = ex.InnerException;
			}

			return ex;
		}

		private static void interpretStatement(StatementSource source, Statement statement, IStatementAction action)
		{
			if (statement != null)
			{
				if (source.PostProcessor != null)
				{
					source.PostProcessor.Process(statement);
				}

				using (Stream statementStream = source.Serializer.Serialize(statement))
				{
					using (Stream ofxStream = source.OfxConverter.ConvertToOfx(statementStream))
					{
						action.DoAction(ofxStream);
					}
				}
			}
		}

		#endregion
	}
}